import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:sqliteasy_builder/sqliteasy_builder.dart';
import 'package:sqliteasy_core/sqliteasy_core.dart' hide ColumnInfo, TableInfo, IndexInfo, ForeignKeyInfo;
import 'package:sqliteasy_core/annotations/database.dart';
import 'package:sqliteasy_core/enums/conflict_strategy.dart';
import '../model/database.dart';
import '../model/dao.dart';
import '../errors.dart';

extension DartObjectExt on DartObject {
  Database toDatabase() {
    final version = getField("version").toIntValue();
    final exportSchema = getField("exportSchema").toBoolValue();
    return Database(null, version, exportSchema: exportSchema, nullValueStrategy: this.toNullValueStrategy());
  }

  Entity toEntity() {
    String tableName = getField("tableName").toStringValue();
    bool inheritSuperIndices = getField("inheritSuperIndices")?.toBoolValue();
    List<String> primaryKeys = getField("primaryKeys")?.toListValue()?.map((e) => e.toStringValue())?.toList();
    List<String> ignoredColumns = getField("ignoredColumns")?.toListValue()?.map((e) => e.toStringValue())?.toList();
    return Entity(
        tableName: tableName,
        indices: null,
        foreignKeys: null,
        ignoredColumns: ignoredColumns,
        inheritSuperIndices: inheritSuperIndices,
        primaryKeys: primaryKeys);
  }

  IndexInfo toIndexInfo() {
    List<String> value = getField("columns")?.toListValue()?.map((e) => e.toStringValue())?.toList();
    String name = getField("name")?.toStringValue();
    bool unique = getField("unique")?.toBoolValue();
    if (null == value) {
      throw SQLiteasyBuildError("The value of Index can not be null");
    }
    return IndexInfo(value, name: name, unique: unique);
  }

  ForeignKeyInfo toForeignKeyInfo() {
    final entityElement = this.getField("entity")?.toTypeValue()?.element;
    final entityInfo = DatabaseUtil.getEntityInfoFromElement(entityElement);
    final referenceColumns = getField("referenceColumns")?.toListValue()?.map((e) => e.toStringValue())?.toList();
    final columns = getField("columns")?.toListValue()?.map((e) => e.toStringValue())?.toList();
    final onDelete = getField("onDelete")?.toAction();
    final onUpdate = getField("onUpdate")?.toAction();
    final deferred = getField("deferred")?.toBoolValue();
    return ForeignKeyInfo(entityInfo, columns, referenceColumns,
        onDelete: onDelete, onUpdate: onUpdate, deferred: deferred);
  }

  ColumnInfo toColumnInfo() {
    String name = getField("name")?.toStringValue();
    String type = getField("type")?.toStringValue();
    int affinity = getField("affinity")?.toIntValue();
    bool notNull = getField("notNull")?.toBoolValue();
    int primaryKeyPosition = getField("primaryKeyPosition")?.toIntValue();
    String defaultValue = getField("defaultValue")?.toStringValue();
    return ColumnInfo(null, name,
        type: type,
        affinity: affinity,
        notNull: notNull,
        primaryKeyPosition: primaryKeyPosition,
        defaultValue: defaultValue);
  }

  PrimaryKeyInfo toPrimaryKeyInfo() {
    bool autoGenerate = getField("autoGenerate")?.toBoolValue();
    int position = getField("position")?.toIntValue();
    return PrimaryKeyInfo(autoGenerate, position);
  }

  UpdateInfo toUpdate() {
    return UpdateInfo(this.toConflictStrategy(), this.toNullValueStrategy());
  }

  InsertInfo toInsert() {
    DartType entity = getField("entity")?.toTypeValue();
    final insertInfo = InsertInfo();
    insertInfo.entity = entity;
    insertInfo.conflictStrategy = this.toConflictStrategy();
    return insertInfo;
  }

  DeleteInfo toDelete() {
    DartType entity = getField("entity")?.toTypeValue();
    return DeleteInfo(entity);
  }

  QueryInfo toQuery() {
    final statement = getField("statement")?.toStringValue();
    return QueryInfo(statement);
  }

  ConflictStrategy toConflictStrategy() => toEnumValue(ConflictStrategy.values);

  NullValueStrategy toNullValueStrategy() => toEnumValue(NullValueStrategy.values);

  Action toAction() => toEnumValue(Action.values);

  E toEnumValue<E>(List<E> values) {
    final index = getField("index")?.toIntValue();
    if (index == null) {
      return null;
    }
    return values[index];
  }
}

extension ColumnExt on Column {}

extension DartTypeExt on DartType {
  bool get isDartCoreCollection {
    return isDartCoreSet || isDartCoreList;
  }
}
